﻿//using Microsoft.EntityFrameworkCore;
//using Microsoft.EntityFrameworkCore.ChangeTracking;
//using Microsoft.EntityFrameworkCore.Design;
//using Microsoft.Extensions.Configuration;
//using Microsoft.Extensions.DependencyInjection;
//using Microsoft.Extensions.Options;
//using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
//using ShardingCore.EFCores.OptionsExtensions;
//using ShardingCore.Extensions;
//using ShardingCore.Sharding.Abstractions;
//using ShardingCore.Sharding.ShardingDbContextExecutors;
//using System;
//using System.Collections.Generic;
//using System.ComponentModel.DataAnnotations.Schema;
//using System.Linq;
//using System.Reflection;
//using System.Threading;
//using System.Threading.Tasks;
//using Volo.Abp;
//using Volo.Abp.Data;
//using Volo.Abp.Domain.Entities;
//using Volo.Abp.EntityFrameworkCore;
//using Volo.Abp.EntityFrameworkCore.Modeling;
//using Volo.Abp.MultiTenancy;
//using Volo.Abp.Reflection;
//using Volo.Abp.Validation;
//using Xms.Abp.Data;
//using CommentAttribute = System.ComponentModel.DataAnnotations.CommentAttribute;
//using IndexAttribute = System.ComponentModel.DataAnnotations.IndexAttribute;

//namespace Xms.Abp.Storage;

///// <summary>
///// 自动分表
///// https://www.cnblogs.com/coldairarrow/archive/2020/04/20/12733886.html
///// https://www.cnblogs.com/hubro/p/12693868.html
///// http://crl.changqidongli.com/
///// https://github.com/Coldairarrow/EFCore.Sharding
///// 分库
///// https://www.cnblogs.com/leolaw/p/10461156.html
///// https://docs.microsoft.com/zh-cn/ef/core/modeling/dynamic-model
///// https://blog.csdn.net/longtenggenssupreme/article/details/110194283
///// 分库、分表、读写分离
///// https://www.cnblogs.com/xuejiaming/p/15173965.html
///// https://xuejm.gitee.io/sharding-core-doc/guide/introduce/
///// https://github.com/xuejmnet/AbpVNextShardingTodoApp
///// </summary>
///// <typeparam name="TStorage"></typeparam>
///// [ReplaceDbContext(typeof(IIdentityDbContext))]
//public abstract class XmsStorage<TStorage>
//    : AbpDbContext<TStorage>, IXmsStorage, IShardingDbContext, ISupportShardingTransaction, ISupportShardingReadWrite
//    where TStorage : XmsStorage<TStorage>
//{
//    protected IServiceProvider ServiceProvider { get; }
//    protected IShardingDbContextExecutor ShardingDbContextExecutor { get; }

//    //public XmsStorage(XmsStorageOptions<TStorage> options)
//    //    : base(options)
//    //{

//    //}
//    public XmsStorage(DbContextOptions<TStorage> options, IServiceProvider serviceProvider)
//        : base(options)
//    {
//        ServiceProvider = serviceProvider;
//        var wrapOptionsExtension = options.FindExtension<ShardingWrapOptionsExtension>();
//        if (wrapOptionsExtension != null)
//        {
//            ShardingDbContextExecutor =
//                (IShardingDbContextExecutor)Activator.CreateInstance(
//                    typeof(ShardingDbContextExecutor<>).GetGenericType0(this.GetType()), this);
//        }
//    }

//    protected virtual void SetTenantIdIfNull(EntityEntry entry)
//    {
//        if (entry.Entity is IMultiTenant entityWithTenantId
//            && entityWithTenantId.TenantId == null
//            && CurrentTenant.Id.HasValue
//            && IsMultiTenantFilterEnabled)
//        {
//            ObjectHelper.TrySetProperty(entityWithTenantId, e => e.TenantId, () => CurrentTenant.Id);
//        }
//    }

//    protected override void ApplyAbpConceptsForAddedEntity(EntityEntry entry)
//    {
//        SetTenantIdIfNull(entry);
//        base.ApplyAbpConceptsForAddedEntity(entry);
//    }

//    protected override void OnModelCreating(ModelBuilder builder)
//    {
//        base.OnModelCreating(builder);

//        var storageType = typeof(TStorage);

//        var storageOptionsType = typeof(IOptions<>).MakeGenericType(typeof(XmsStorageConfigurationOptions<>).MakeGenericType(storageType));
//        var storageOptions = ServiceProvider.GetRequiredService(storageOptionsType) as IOptions<AbpModelBuilderConfigurationOptions>;

//        var tables = storageType.GetProperties().Where(r => r.PropertyType.IsGenericType && r.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)).Select(r => r.PropertyType.GenericTypeArguments.First());

//        foreach (var table in tables)
//        {
//            var tableOptionsType = typeof(IOptions<>).MakeGenericType(typeof(XmsStorageEntityConfigurationOptions<>).MakeGenericType(table));
//            var tableOptions = ServiceProvider.GetRequiredService(storageOptionsType) as IOptions<AbpModelBuilderConfigurationOptions>;

//            if (tableOptions.Value == null || tableOptions.Value.TablePrefix.IsNullOrWhiteSpace())
//            {
//                tableOptions = storageOptions;
//            }

//            builder.Entity(table, b =>
//            {
//                var tableAttr = table.GetCustomAttribute<TableAttribute>();
//                var tableComentAttr = table.GetCustomAttribute<CommentAttribute>();
//                var tableName = (tableOptions.Value?.TablePrefix ?? "") + (tableAttr?.Name ?? table.Name).Replace("yEntity", "ies").Replace("esEntity", "es").Replace("sEntity", "es").Replace("Entity", "s").Replace("yModel", "ies").Replace("esModel", "es").Replace("sModel", "es").Replace("Model", "s");
//                var schemaName = tableOptions.Value?.Schema ?? tableAttr?.Schema;
//                b.ToTable(tableName, schemaName);

//                if (tableComentAttr != null)
//                {
//                    b.HasComment(tableComentAttr.Comment);
//                }
//                b.ConfigureByConventionWithIndex();

//                var properties = table.GetProperties().Where(r => r.GetCustomAttribute<CommentAttribute>() != null);

//                foreach (var property in properties)
//                {
//                    var propertyComentAttr = property.GetCustomAttribute<CommentAttribute>();
//                    b.Property(property.Name).HasComment(propertyComentAttr.Comment);
//                }

//                var indexAttrs = table.GetCustomAttributes<IndexAttribute>();
//                foreach (var indexAttr in indexAttrs)
//                {
//                    var pns = indexAttr.PropertyNames.ToArray();
//                    if (!indexAttr.Name.IsNullOrWhiteSpace())
//                    {
//                        b.HasIndex(pns, indexAttr.Name);
//                    }
//                    else
//                    {
//                        b.HasIndex(pns);
//                    }
//                }

//                b.ApplyObjectExtensionMappings();
//            });
//        }
//    }

//    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
//    {
//        base.OnConfiguring(optionsBuilder);

//    }
//    /// <summary>
//    /// 读写分离优先级
//    /// </summary>
//    public int ReadWriteSeparationPriority
//    {
//        get => ShardingDbContextExecutor.ReadWriteSeparationPriority;
//        set => ShardingDbContextExecutor.ReadWriteSeparationPriority = value;
//    }
//    /// <summary>
//    /// 是否使用读写分离
//    /// </summary>
//    public bool ReadWriteSeparation
//    {
//        get => ShardingDbContextExecutor.ReadWriteSeparation;
//        set => ShardingDbContextExecutor.ReadWriteSeparation = value;
//    }

//    /// <summary>
//    /// 是否是真正的执行者
//    /// </summary>
//    private bool IsExecutor => ShardingDbContextExecutor == null;

//    //public void ShardingUpgrade()
//    //{
//    //    //isExecutor = true;
//    //}

//    public DbContext GetDbContext(string dataSourceName, bool parallelQuery, IRouteTail routeTail)
//    {
//        var dbContext = ShardingDbContextExecutor.CreateDbContext(parallelQuery, dataSourceName, routeTail);
//        if (dbContext is AbpDbContext<TStorage> abpDbContext)
//        {
//            abpDbContext.LazyServiceProvider = this.LazyServiceProvider;
//        }

//        return dbContext;
//    }

//    /// <summary>
//    /// 根据对象创建通用的dbcontext
//    /// </summary>
//    /// <typeparam name="TEntity"></typeparam>
//    /// <param name="entity"></param>
//    /// <returns></returns>
//    public DbContext CreateGenericDbContext<TEntity>(TEntity entity) where TEntity : class
//    {
//        CheckAndSetShardingKeyThatSupportAutoCreate(entity);
//        var dbContext = ShardingDbContextExecutor.CreateGenericDbContext(entity);
//        if (dbContext is AbpDbContext<TStorage> abpDbContext && abpDbContext.LazyServiceProvider == null)
//        {
//            abpDbContext.LazyServiceProvider = this.LazyServiceProvider;
//        }

//        return dbContext;
//    }

//    private void CheckAndSetShardingKeyThatSupportAutoCreate<TEntity>(TEntity entity) where TEntity : class
//    {
//        if (entity is IShardingKeyIsGuId)
//        {
//            if (entity is IEntity<Guid> guidEntity)
//            {
//                if (guidEntity.Id != default)
//                {
//                    return;
//                }
//                var idProperty = entity.GetProperty(nameof(IEntity<Guid>.Id));

//                var dbGeneratedAttr = ReflectionHelper
//                    .GetSingleAttributeOrDefault<DatabaseGeneratedAttribute>(
//                        idProperty
//                    );

//                if (dbGeneratedAttr != null && dbGeneratedAttr.DatabaseGeneratedOption != DatabaseGeneratedOption.None)
//                {
//                    return;
//                }

//                EntityHelper.TrySetId(
//                    guidEntity,
//                    () => GuidGenerator.Create(),
//                    true
//                );
//            }
//        }
//        else if (entity is IShardingKeyIsCreationTime)
//        {
//            AuditPropertySetter?.SetCreationProperties(entity);
//        }
//    }


//    public override EntityEntry Add(object entity)
//    {
//        if (IsExecutor)
//            base.Add(entity);
//        return CreateGenericDbContext(entity).Add(entity);
//    }

//    public override EntityEntry<TEntity> Add<TEntity>(TEntity entity)
//    {
//        if (IsExecutor)
//            return base.Add(entity);
//        return CreateGenericDbContext(entity).Add(entity);
//    }


//    public override ValueTask<EntityEntry<TEntity>> AddAsync<TEntity>(TEntity entity, CancellationToken cancellationToken = new CancellationToken())
//    {
//        if (IsExecutor)
//            return base.AddAsync(entity, cancellationToken);
//        return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
//    }

//    public override ValueTask<EntityEntry> AddAsync(object entity, CancellationToken cancellationToken = new CancellationToken())
//    {
//        if (IsExecutor)
//            return base.AddAsync(entity, cancellationToken);
//        return CreateGenericDbContext(entity).AddAsync(entity, cancellationToken);
//    }

//    public override void AddRange(params object[] entities)
//    {
//        if (IsExecutor)
//        {
//            base.AddRange(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            group.Key.AddRange(group.Select(o => o.Entity));
//        }
//    }

//    public override void AddRange(IEnumerable<object> entities)
//    {
//        if (IsExecutor)
//        {
//            base.AddRange(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            group.Key.AddRange(group.Select(o => o.Entity));
//        }
//    }

//    public override async Task AddRangeAsync(params object[] entities)
//    {
//        if (IsExecutor)
//        {
//            await base.AddRangeAsync(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            await group.Key.AddRangeAsync(group.Select(o => o.Entity));
//        }
//    }

//    public override async Task AddRangeAsync(IEnumerable<object> entities, CancellationToken cancellationToken = new CancellationToken())
//    {
//        if (IsExecutor)
//        {
//            await base.AddRangeAsync(entities, cancellationToken);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            await group.Key.AddRangeAsync(group.Select(o => o.Entity));
//        }
//    }

//    public override EntityEntry<TEntity> Attach<TEntity>(TEntity entity)
//    {
//        if (IsExecutor)
//            return base.Attach(entity);
//        return CreateGenericDbContext(entity).Attach(entity);
//    }

//    public override EntityEntry Attach(object entity)
//    {
//        if (IsExecutor)
//            return base.Attach(entity);
//        return CreateGenericDbContext(entity).Attach(entity);
//    }

//    public override void AttachRange(params object[] entities)
//    {
//        if (IsExecutor)
//        {
//            base.AttachRange(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            group.Key.AttachRange(group.Select(o => o.Entity));
//        }
//    }

//    public override void AttachRange(IEnumerable<object> entities)
//    {
//        if (IsExecutor)
//        {
//            base.AttachRange(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            group.Key.AttachRange(group.Select(o => o.Entity));
//        }
//    }


//    //public override DatabaseFacade Database => _dbContextCaches.Any()
//    //    ? _dbContextCaches.First().Value.Database
//    //    : GetDbContext(true, string.Empty).Database;

//    public override EntityEntry<TEntity> Entry<TEntity>(TEntity entity)
//    {
//        if (IsExecutor)
//            return base.Entry(entity);
//        return CreateGenericDbContext(entity).Entry(entity);
//    }

//    public override EntityEntry Entry(object entity)
//    {
//        if (IsExecutor)
//            return base.Entry(entity);
//        return CreateGenericDbContext(entity).Entry(entity);
//    }

//    public override EntityEntry<TEntity> Update<TEntity>(TEntity entity)
//    {
//        if (IsExecutor)
//            return base.Update(entity);
//        return CreateGenericDbContext(entity).Update(entity);
//    }

//    public override EntityEntry Update(object entity)
//    {
//        if (IsExecutor)
//            return base.Update(entity);
//        return CreateGenericDbContext(entity).Update(entity);
//    }

//    public override void UpdateRange(params object[] entities)
//    {
//        if (IsExecutor)
//        {
//            base.UpdateRange(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            group.Key.UpdateRange(group.Select(o => o.Entity));
//        }
//    }

//    public override void UpdateRange(IEnumerable<object> entities)
//    {
//        if (IsExecutor)
//        {
//            base.UpdateRange(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            group.Key.UpdateRange(group.Select(o => o.Entity));
//        }
//    }

//    public override EntityEntry<TEntity> Remove<TEntity>(TEntity entity)
//    {
//        if (IsExecutor)
//            return base.Remove(entity);
//        return CreateGenericDbContext(entity).Remove(entity);
//    }

//    public override EntityEntry Remove(object entity)
//    {
//        if (IsExecutor)
//            return base.Remove(entity);
//        return CreateGenericDbContext(entity).Remove(entity);
//    }

//    public override void RemoveRange(params object[] entities)
//    {
//        if (IsExecutor)
//        {
//            base.RemoveRange(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            group.Key.RemoveRange(group.Select(o => o.Entity));
//        }
//    }

//    public override void RemoveRange(IEnumerable<object> entities)
//    {
//        if (IsExecutor)
//        {
//            base.RemoveRange(entities);
//            return;
//        }
//        var groups = entities.Select(o =>
//        {
//            var dbContext = CreateGenericDbContext(o);
//            return new
//            {
//                DbContext = dbContext,
//                Entity = o
//            };
//        }).GroupBy(g => g.DbContext);

//        foreach (var group in groups)
//        {
//            group.Key.RemoveRange(group.Select(o => o.Entity));
//        }
//    }

//    public override int SaveChanges()
//    {

//        if (IsExecutor)
//            return base.SaveChanges();
//        return this.SaveChanges(true);
//    }

//    public override int SaveChanges(bool acceptAllChangesOnSuccess)
//    {
//        if (IsExecutor)
//            return base.SaveChanges(acceptAllChangesOnSuccess);
//        //ApplyShardingConcepts();
//        int i = 0;
//        //如果是内部开的事务就内部自己消化
//        if (Database.CurrentTransaction == null && ShardingDbContextExecutor.IsMultiDbContext)
//        {
//            using (var tran = Database.BeginTransaction())
//            {
//                i = ShardingDbContextExecutor.SaveChanges(acceptAllChangesOnSuccess);
//                tran.Commit();
//            }
//        }
//        else
//        {
//            i = ShardingDbContextExecutor.SaveChanges(acceptAllChangesOnSuccess);
//        }

//        return i;
//    }


//    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
//    {
//        if (IsExecutor)
//            return base.SaveChangesAsync(cancellationToken);
//        return this.SaveChangesAsync(true, cancellationToken);
//    }

//    public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
//    {
//        if (IsExecutor)
//            return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
//        //ApplyShardingConcepts();
//        int i = 0;
//        //如果是内部开的事务就内部自己消化
//        if (Database.CurrentTransaction == null && ShardingDbContextExecutor.IsMultiDbContext)
//        {
//            using (var tran = await Database.BeginTransactionAsync(cancellationToken))
//            {
//                i = await ShardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);

//                await tran.CommitAsync(cancellationToken);
//            }
//        }
//        else
//        {
//            i = await ShardingDbContextExecutor.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
//        }


//        return i;
//    }

//    public override void Dispose()
//    {

//        if (IsExecutor)
//        {
//            base.Dispose();
//        }
//        else
//        {
//            ShardingDbContextExecutor.Dispose();
//            base.Dispose();
//        }
//    }

//    public override async ValueTask DisposeAsync()
//    {
//        if (IsExecutor)
//        {
//            await base.DisposeAsync();
//        }
//        else
//        {
//            await ShardingDbContextExecutor.DisposeAsync();

//            await base.DisposeAsync();
//        }
//    }
//    public Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken())
//    {
//        return ShardingDbContextExecutor.RollbackAsync(cancellationToken);
//    }

//    public Task CommitAsync(CancellationToken cancellationToken = new CancellationToken())
//    {
//        return ShardingDbContextExecutor.CommitAsync(cancellationToken);
//    }

//    public void NotifyShardingTransaction()
//    {
//        ShardingDbContextExecutor.NotifyShardingTransaction();
//    }

//    public void Rollback()
//    {
//        ShardingDbContextExecutor.Rollback();
//    }

//    public void Commit()
//    {
//        ShardingDbContextExecutor.Commit();
//    }

//    public class StorageDesignTimeFactory
//        : IDesignTimeDbContextFactory<TStorage>
//    {
//        public TStorage CreateDbContext(string[] args)
//        {
//            var configuration = BuildConfiguration();

//            var type = typeof(TStorage);
//            var dbType = configuration["DbContextOptions:DefaultDbType"]?.ToLower() ?? "mysql";

//            var connectionStringKey = type.GetCustomAttribute<ConnectionStringNameAttribute>()?.Name ?? "Default";

//            var connectionString = configuration.GetConnectionString(connectionStringKey) ?? configuration.GetConnectionString("Default");

//            Console.WriteLine(type.Name + "'s connectiong string is:" + connectionString);

//            var builder = new DbContextOptionsBuilder<TStorage>();
//            var assemblyName = GetType().Assembly.FullName;
//            builder = dbType switch
//            {
//                "postgresql" => builder.UseNpgsql(connectionString, b => b.MigrationsAssembly(assemblyName)),
//                "sqlserver" => builder.UseSqlServer(connectionString, b => b.MigrationsAssembly(assemblyName)),
//                //"oracle" => (DbContextOptionsBuilder<TStorage>)builder.UseOracle(connectionString, b => b.MigrationsAssembly(assemblyName)),
//                "sqlite" => builder.UseSqlite(connectionString, b => b.MigrationsAssembly(assemblyName)),
//                "mysql" => builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), b => b.MigrationsAssembly(assemblyName)),
//                _ => builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), b => b.MigrationsAssembly(assemblyName)),
//            };

//            var services = new ServiceCollection();
//            services.Configure<XmsStorageConfigurationOptions<TStorage>>(options => { });
//            ConfigureServices(services);
//            var serviceProvider = services.BuildServiceProvider();

//            var storage = (TStorage)Activator.CreateInstance(typeof(TStorage), builder.Options, serviceProvider);

//            return storage;
//        }
//        private static IConfigurationRoot BuildConfiguration()
//        {
//            var builder = new ConfigurationBuilder()
//                .AddJsonFile("appsettings.json", optional: false);

//            return builder.Build();
//        }

//        protected virtual void ConfigureServices(IServiceCollection services)
//        {

//        }
//    }
//}
